package io.hellsinger.vortex.ui.component.storage.standard.linear

import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.content.Context
import android.content.res.ColorStateList.valueOf
import android.graphics.Canvas
import android.graphics.drawable.Drawable
import android.graphics.drawable.RippleDrawable
import android.text.TextUtils
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.FrameLayout.LayoutParams.WRAP_CONTENT
import android.widget.ImageView
import android.widget.ImageView.ScaleType
import android.widget.TextView
import androidx.annotation.DrawableRes
import androidx.core.view.contains
import io.hellsinger.theme.Colors
import io.hellsinger.vortex.R
import io.hellsinger.vortex.bit.Bits.Companion.hasFlag
import io.hellsinger.vortex.bit.Bits.Companion.removeFlag
import io.hellsinger.vortex.data.model.PathItem
import io.hellsinger.vortex.ui.component.Paints
import io.hellsinger.vortex.ui.component.ViewMeasure
import io.hellsinger.vortex.ui.component.adapter.holder.RecyclableView
import io.hellsinger.vortex.ui.component.dp
import io.hellsinger.vortex.ui.component.drawable.RoundedCornerDrawable
import io.hellsinger.vortex.ui.component.storage.StorageAdapter.Companion.UPDATE_ITEM_SELECTION
import io.hellsinger.vortex.ui.component.storage.StorageAdapter.Companion.UPDATE_WITHOUT_ANIMATION
import io.hellsinger.vortex.ui.component.storage.StorageCell
import io.hellsinger.vortex.ui.dsl.frameParams
import java.lang.Math.toRadians
import kotlin.math.abs
import kotlin.math.cos
import kotlin.math.sin

class StandardStorageLinearCell(
    context: Context,
) : FrameLayout(context),
    RecyclableView<PathItem>,
    StorageCell {
    private var animator: ValueAnimator? =
        ValueAnimator.ofFloat(0F, 1F).apply {
            duration = DEFAULT_STATE_SWITCH_DURATION
        }

    private val largeInnerPadding = 16.dp
    private val middleInnerPadding = 8.dp

    private val titlePadding = 24.dp
    private val subtitlePadding = 24.dp

    private val iconSize = 40.dp

    private var selectionFactor = 0F
        set(value) {
            if (field == value) return
            field = value
            invalidate()
        }

    private val containsTitle: Boolean
        get() = contains(titleView)

    private val containsSubtitle: Boolean
        get() = contains(subtitleView)

    private val containsIcon: Boolean
        get() = contains(iconView)

    private val iconSurface =
        RoundedCornerDrawable(
            color = Colors.Gray,
            radius = 50F.dp,
        )

    private val surface =
        RoundedCornerDrawable(
            color = Colors.Black,
            radius = 0F,
        )

    private val ripple =
        RippleDrawable(
            valueOf(0),
            null,
            surface,
        )

    private val iconRipple =
        RippleDrawable(
            valueOf(0),
            iconSurface,
            null,
        )

    private val iconView =
        ImageView(context).apply {
            layoutParams = LayoutParams(iconSize, iconSize)
            scaleType = ScaleType.CENTER_INSIDE
            id = R.id.file_manager_list_item_icon

            background = iconRipple
        }

    private val titleView =
        TextView(context).apply {
            textSize = 16F
            isSingleLine = true
            maxLines = 1
            ellipsize = TextUtils.TruncateAt.MARQUEE
            marqueeRepeatLimit = -1
            frameParams(WRAP_CONTENT, WRAP_CONTENT)
        }

    private val subtitleView =
        TextView(context).apply {
            textSize = 14F
            isSingleLine = true
            maxLines = 1
            ellipsize = TextUtils.TruncateAt.MARQUEE
            marqueeRepeatLimit = -1
            frameParams(WRAP_CONTENT, WRAP_CONTENT)
        }

    override var title: String?
        get() = titleView.text.toString()
        set(value) {
            ensureContainingTitle()
            titleView.text = value
        }

    override var subtitle: String?
        get() = subtitleView.text.toString()
        set(value) {
            ensureContainingInfo()
            subtitleView.text = value
        }

    override var icon: Drawable?
        get() = iconView.drawable
        set(value) {
            ensureContainingIcon()
            iconView.setImageDrawable(value)
        }

    override var flag: Int = 0
        set(value) {
            if (value == field) return
            field = value removeFlag UPDATE_WITHOUT_ANIMATION
            checkSelectionAnimator(value)
        }

    private var selectionColor = 0

    private fun ensureContainingTitle() {
        if (!containsTitle) addView(titleView)
    }

    private fun ensureContainingInfo() {
        if (!containsSubtitle) {
            addView(subtitleView)
        }
    }

    private fun ensureContainingIcon() {
        if (!containsIcon) {
            addView(iconView)
        }
    }

    fun setIcon(
        @DrawableRes id: Int,
    ) {
        ensureContainingIcon()
        iconView.setImageResource(id)
    }

    init {
        id = R.id.file_manager_list_item_root
        isClickable = true
        isFocusable = true
        clipToOutline = true

        background = surface
        foreground = ripple
    }

    fun setOnIconClickListener(listener: OnClickListener?) {
        iconView.setOnClickListener(listener)
    }

    fun setOnIconLongClickListener(listener: OnLongClickListener?) {
        iconView.setOnLongClickListener(listener)
    }

    override fun onBindListener(listener: OnClickListener?) {
        setOnClickListener(listener)
        setOnIconClickListener(listener)
    }

    override fun onBindLongListener(listener: OnLongClickListener?) {
        setOnLongClickListener(listener)
        setOnIconLongClickListener(listener)
    }

    override fun setTag(tag: Any?) {
        super.setTag(tag)
        iconView.tag = tag
    }

    override fun setBackgroundColor(color: Int) {
        surface.setColor(color)
    }

    fun setTitleColor(color: Int) {
        titleView.setTextColor(color)
    }

    fun setSubtitleColor(color: Int) {
        subtitleView.setTextColor(color)
    }

    fun setIconColor(color: Int) {
        iconView.imageTintList = valueOf(color)
    }

    fun setIconSurfaceColor(color: Int) {
        iconSurface.setColor(color)
    }

    fun setSurfaceRippleColor(color: Int) {
        ripple.setColor(valueOf(color))
    }

    fun setIconSurfaceRippleColor(color: Int) {
        iconRipple.setColor(valueOf(color))
    }

    fun setSelectionColor(color: Int) {
        selectionColor = color
    }

    override fun onDrawForeground(canvas: Canvas) {
        val radians = toRadians(45.0)
        val radius = 8F.dp
        val centerX = ((iconView.left + iconView.right) / 2) + (iconView.width / 2 * sin(radians))
        val centerY = ((iconView.top + iconView.bottom) / 2) + (iconView.height / 2 * cos(radians))

        canvas.drawCircle(
            centerX.toFloat(),
            centerY.toFloat(),
            radius * selectionFactor,
            Paints.filling(selectionColor),
        )
        super.onDrawForeground(canvas)
    }

    override fun onMeasure(
        widthMeasureSpec: Int,
        heightMeasureSpec: Int,
    ) {
        ViewMeasure(
            widthSpec = widthMeasureSpec,
            heightSpec = heightMeasureSpec,
            desireWidth = ViewGroup.LayoutParams.MATCH_PARENT,
            desireHeight = 56,
            ::setMeasuredDimension,
        )

        measureChildren(widthMeasureSpec, heightMeasureSpec)
    }

    override fun onLayout(
        changed: Boolean,
        left: Int,
        top: Int,
        right: Int,
        bottom: Int,
    ) {
        var widthLeft = largeInnerPadding
        if (containsIcon) {
            iconView.layout(
                widthLeft,
                (measuredHeight - iconView.measuredHeight) shr 1,
                widthLeft + iconView.measuredWidth,
                ((measuredHeight - iconView.measuredHeight) shr 1) + iconView.measuredHeight,
            )
            widthLeft += iconView.measuredWidth
        }
        var titleY = if (!containsSubtitle) largeInnerPadding else middleInnerPadding
        if (containsTitle) {
            titleView.layout(
                widthLeft + titlePadding,
                titleY,
                widthLeft + titlePadding + titleView.measuredWidth,
                titleY + titleView.measuredHeight,
            )
            titleY += titleView.measuredHeight
        }
        if (containsSubtitle) {
            subtitleView.layout(
                widthLeft + subtitlePadding,
                titleY,
                widthLeft + subtitlePadding + subtitleView.measuredWidth,
                titleY + subtitleView.measuredHeight,
            )
        }
    }

    private fun checkSelectionAnimator(flag: Int) {
        val isSelected = flag hasFlag UPDATE_ITEM_SELECTION

        if (flag hasFlag UPDATE_WITHOUT_ANIMATION) {
            selectionFactor = if (isSelected) 1F else 0F
            return
        }

        createSelectionAnimator(isSelected).start()
    }

    private fun createSelectionAnimator(isSelected: Boolean): ValueAnimator {
        val animator = createStateAnimator()
        animator.addUpdateListener { anim ->
            selectionFactor =
                if (isSelected) anim.animatedFraction else selectionFactor - anim.animatedFraction
        }
        return animator
    }

    fun animateInfoUpdate(text: String?) {
        if (subtitle == text) return
        createInfoAnimator(text).start()
    }

    private fun createInfoAnimator(text: String?): ValueAnimator {
        val animator =
            createStateAnimator(
                onEnd = { anim, isReverse ->
                    subtitleView.alpha = 1F
                    subtitle = text
                },
                onCancel = { anim ->
                    subtitleView.alpha = 1F
                    subtitle = text
                },
            )
        animator.addUpdateListener { anim ->
            subtitleView.alpha = abs(2 * anim.animatedFraction - 1F)
            if (anim.animatedFraction >= 0.5F) subtitle = text
        }
        return animator
    }

    private inline fun createStateAnimator(
        crossinline onEnd: (Animator, Boolean) -> Unit = { _, _ -> },
        crossinline onCancel: (Animator) -> Unit = {},
    ): ValueAnimator {
        animator?.cancel()

        animator =
            ValueAnimator.ofFloat(0F, 1F).apply {
                duration = DEFAULT_STATE_SWITCH_DURATION
                addListener(
                    object : AnimatorListenerAdapter() {
                        override fun onAnimationCancel(animation: Animator) {
                            onCancel(animation)
                            animator = null
                        }

                        override fun onAnimationEnd(
                            animation: Animator,
                            isReverse: Boolean,
                        ) {
                            onEnd(animation, isReverse)
                            animator = null
                        }
                    },
                )
            }
        return animator!!
    }

    companion object {
        private const val DEFAULT_STATE_SWITCH_DURATION = 200L
    }
}
